package com.daxue.configtest.others;

import lombok.AllArgsConstructor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;

/**
 * @author daxue0929
 * @date 2023/3/22
 */

public class CollectionsUtils {
    /**
     * short cuts to generate unmodifiable set
     */
    public static <E> Set<E> setOf() {
        return setOf(emptySet());
    }

    public static <E> Set<E> setOf(E e1) {
        return setOf(ofList(e1));
    }

    public static <E> Set<E> setOf(E e1, E e2) {
        return setOf(ofList(e1, e2));
    }

    public static <E> Set<E> setOf(E e1, E e2, E e3) {
        return setOf(ofList(e1, e2, e3));
    }

    public static <E> Set<E> setOf(E e1, E e2, E e3, E e4) {
        return setOf(ofList(e1, e2, e3, e4));
    }

    public static <E> Set<E> setOf(Collection<E> elementCollection) {
        return setOf(new HashSet<>(elementCollection));
    }

    public static <E> Set<E> setOf(Set<E> elements) {
        return unmodifiableSet(elements);
    }

    /**
     * short cuts to generate unmodifiable list
     */
    public static <E> List<E> listOf() {
        return listOf(emptyList());
    }

    public static <E> List<E> listOf(E e1) {
        return listOf(ofList(e1));
    }

    public static <E> List<E> listOf(E e1, E e2) {
        return listOf(ofList(e1, e2));
    }

    public static <E> List<E> listOf(E e1, E e2, E e3) {
        return listOf(ofList(e1, e2, e3));
    }

    public static <E> List<E> listOf(E e1, E e2, E e3, E e4) {
        return listOf(ofList(e1, e2, e3, e4));
    }

    public static <E> List<E> listOf(Collection<E> elementCollection) {
        return listOf(new ArrayList<>(elementCollection));
    }

    public static <E> List<E> listOf(List<E> elements) {
        return unmodifiableList(elements);
    }

    /**
     * Short cuts to generate unmodifiable map
     */
    public static <K, V> Map<K, V> mapOf() {
        return mapOf(emptyMap());
    }

    public static <K, V> Map<K, V> mapOf(K k1, V v1) {
        return mapOf(ofList(MapEntry.of(k1, v1)));
    }

    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2) {
        return mapOf(ofList(MapEntry.of(k1, v1), MapEntry.of(k2, v2)));
    }

    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3) {
        return mapOf(ofList(MapEntry.of(k1, v1), MapEntry.of(k2, v2), MapEntry.of(k3, v3)));
    }

    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
        return mapOf(ofList(MapEntry.of(k1, v1), MapEntry.of(k2, v2), MapEntry.of(k3, v3), MapEntry.of(k4, v4)));
    }

    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
        return mapOf(ofList(
            MapEntry.of(k1, v1),
            MapEntry.of(k2, v2),
            MapEntry.of(k3, v3),
            MapEntry.of(k4, v4),
            MapEntry.of(k5, v5)
        ));
    }

    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
        return mapOf(ofList(
            MapEntry.of(k1, v1),
            MapEntry.of(k2, v2),
            MapEntry.of(k3, v3),
            MapEntry.of(k4, v4),
            MapEntry.of(k5, v5),
            MapEntry.of(k6, v6)
        ));
    }

    public static <K, V> Map<K, V> mapOf(Map<K, V> map) {
        return unmodifiableMap(map);
    }

    private static <K, V> Map<K, V> mapOf(Iterable<Map.Entry<K, V>> entries) {
        Map<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return mapOf(map);
    }

    /**
     * helper function
     */
    @SafeVarargs
    private static <T> List<T> ofList(T... elements) {
        return Arrays.asList(elements);
    }

    public static <T> Collection<List<T>> partition(Collection<T> collection, int partitionSize) {
        AlertUtils.notNull(collection);
        AlertUtils.isTrue(partitionSize > 0);
        final AtomicInteger index = new AtomicInteger();
        return collection.stream()
            .collect(Collectors.groupingBy(e -> index.getAndIncrement() / partitionSize))
            .values();
    }

    @AllArgsConstructor(staticName = "of")
    private static class MapEntry<K, V> implements Map.Entry<K, V> {

        private final K key;
        private V value;

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(final V value) {
            V old = this.value;
            this.value = value;
            return old;
        }
    }
}
